#pragma once
#include <lcir/ADT/IList.h>
#include <lcir/ADT/ListNode.h>
#include <lcir/ADT/OwningRefVector.h>
#include <lcir/IR/LambdaBase.h>
#include <lcir/IR/Op.h>
#include <lcir/IR/Operand.h>
#include <lcir/IR/Prototype.h>

#include <initializer_list>
#include <iterator>
#include <memory>
#include <optional>
#include <stdexcept>
#include <string>
#include <utility>
#include <vector>
using namespace std::literals;
namespace lcir {
class ExprImpl;
class Value;
class Constant;
class Type;
// Expression掌管Op、result以及Operand的生命周期
// Todo: 这里想办法处理下循环依赖
class Expression final : public NestListNode<Expression, LambdaBase> {
  friend class LambdaBase;
  // 指向父lambda节点
  LambdaBase *parent{nullptr};
  OwningRefVector<Value> results;
  OwningRefVector<Operand> operands;
  std::unique_ptr<Op> op;
  Expression(Op *op, const std::vector<Prototype *> retProtos, const std::vector<Value *> &operandVals);

 public:
  ~Expression();
  LambdaBase *getParent() const { return parent; }
  /**
   * @brief 获取当前expression所在的lambda节点
   */
  Lambda *getLambda() const;
  static std::unique_ptr<Expression> get(std::unique_ptr<Op> op, const std::vector<Value *> &operandVals) {
    [[unlikely]] if (!op->checkType(operandVals))
      throw std::runtime_error("op type error: " + op->getName() + "!"s);
    auto retProtos = op->deduceRetType(operandVals);
    auto *opPtr = op.release();
    auto *expr = new Expression(opPtr, retProtos, operandVals);
    opPtr->setExpr(expr);
    expr->handleOperandValues(operandVals);
    return std::unique_ptr<Expression>(expr);
  }

  template <class OpTy, class... OpArgs>
  static std::unique_ptr<Expression> get(const std::vector<Value *> &operandVals, OpArgs... args) {
    [[unlikely]] if (!OpTy::get(args...))
      throw std::runtime_error("error get method!");
    auto op = OpTy::template get<OpArgs...>(args...);
    return get(std::move(op), operandVals);
  }

  template <class OpTy, class... OpArgs>
  static std::unique_ptr<Expression> get(OpArgs... args) {
    // [[unlikely]] if (!OpTy::get(args...))
    //   throw std::runtime_error("error get method!");
    auto op = OpTy::template get<OpArgs...>(args...);
    return get(std::move(op), {});
  }
  Constant *tryFold();

  template <class ExprTemp>
  bool hasTemp() const {
    return op->hasTemp<ExprTemp>();
  }

  template <class ExprTemp>
  std::optional<ExprTemp> getTemp() {
    if (!hasTemp<ExprTemp>()) return std::nullopt;
    return ExprTemp(this);
  }

  Value *findTopParent() const;
  [[nodiscard]] std::unique_ptr<Expression> removeFromLambda();
  void eraseFromLambda();

  template <class OpTy>
  bool isOp() const {
    return op->isa<OpTy>();
  }
  template <class OpTy>
  OpTy *getOp() const {
    return op->dyn_cast<OpTy>();
  }

  Op *getOp() const;
  OpInstance *getOpInstance() const;
  Operand *getOperand(size_t idx) const;
  Value *getOperandValue(size_t idx) const;
  std::vector<Value *> getOperandValues() const;
  void setOperand(size_t idx, Value *val);
  std::vector<Operand *> getOperands() const;
  // 这个返回的是第一个result，若无返回值，返回为空
  Value *getResult() const;
  Value *getResult(size_t idx) const;
  // 返回result包
  std::vector<Value *> getResults() const;
  // 返回result proto
  // std::vector<Prototype *> getResultProtos() const;
  // 返回result proto
  Prototype *getResultProto() const;
  template <class ProtoTy>
  ProtoTy *getResultProto() const {
    return getResultProto()->dyn_cast<ProtoTy>();
  }

  /**
   * @brief 把一个expression插入到当前expression的后面
   */
  Expression *insertBack(std::unique_ptr<Expression> expr);
  /**
   * @brief 把一个expression插入到当前expression的前面
   */
  Expression *insertFront(std::unique_ptr<Expression> expr);

  // 返回当前expression的深度
  size_t getDepth() const;

  /**
   * @brief 打印成string
   */
  std::string print() const;
  void dump() const;

 private:
  void addOperand(Value *val);
  void addOperands(const std::vector<Value *> &operandVals);
  void addResults(const std::vector<Prototype *> &protos);
  void setParent(LambdaBase *parent);
  void handleOperandValues(const std::vector<Value *> &operandVals);
  void clearOperands();
};
};  // namespace lcir